#include <iostream>
// #include <libswifft/swifft_object.h>
#include "libswifft/swifft.hpp"
#include "libswifft/swifft_avx2.h"
#include <chrono>

using namespace LibSwifft;
using namespace std;

static void randomize(SwifftInput* input, size_t size)
{
    for (size_t i = 0; i < size; i++) {
        int* data = (int*)(input[i].data);
        for (size_t j = 0; j < SWIFFT_INPUT_BLOCK_SIZE / sizeof(int); j++)
            data[j] = rand();
    }
}

static void char_to_hex(BitSequence* src, int len, char* des)
{
    char hex_table[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    while (len--) {
        *(des++) = hex_table[(*src) >> 4];
        *(des++) = hex_table[*(src++) & 0x0f];
    }
}

const int blkNum = 4 * 1024 * 1024;

SwifftInput input[blkNum];
SwifftOutput output[blkNum];

int main()
{
    /* initialize object APIs once, possibly inside a function: */
    // swifft_object_t swifft;
    // SWIFFT_InitObject(&swifft);
    /* later, inside a function: */
    // SwifftInput input1 = { 0 }, input2 = { 0 }, input3 = { 0 }, p = { 0 };
    // SwifftOutput output1, output2, output3, phash, outhash = { 0 };

    // // input1.data[0] = 1;
    // // input2.data[0] = 3;
    // // input3.data[0] = 3;
    // randomize(&input1, 1);
    // randomize(&input2, 1);
    // // randomize(&input3, 1);
    // /* hash operations are available via swifft.hash, for example: */
    // SWIFFT_Compute(input1.data, output1.data);
    // SWIFFT_Compute(input2.data, output2.data);
    // // SWIFFT_Compute(input3.data, output3.data);
    // for (int i = 0; i < SWIFFT_INPUT_BLOCK_SIZE; i++) {
    //     p.data[i] = 0;
    //     p.data[i] += input1.data[i];
    //     p.data[i] += input2.data[i];
    //     // p.data[i] += input3.data[i];
    // }
    // SWIFFT_Compute(p.data, phash.data);

    // char outhash_hexchar[SWIFFT_OUTPUT_BLOCK_SIZE << 1 + 1];
    // char_to_hex(output1.data, SWIFFT_OUTPUT_BLOCK_SIZE, outhash_hexchar);
    // outhash_hexchar[SWIFFT_OUTPUT_BLOCK_SIZE << 1] = '\0';
    // cout << outhash_hexchar << endl;

    // // char outhash_hexchar[SWIFFT_OUTPUT_BLOCK_SIZE << 1 + 1];
    // char_to_hex(output2.data, SWIFFT_OUTPUT_BLOCK_SIZE, outhash_hexchar);
    // outhash_hexchar[SWIFFT_OUTPUT_BLOCK_SIZE << 1] = '\0';
    // cout << outhash_hexchar << endl;

    // char phash_hexchar[SWIFFT_OUTPUT_BLOCK_SIZE << 1 + 1];
    // char_to_hex(phash.data, SWIFFT_OUTPUT_BLOCK_SIZE, phash_hexchar);
    // phash_hexchar[SWIFFT_OUTPUT_BLOCK_SIZE << 1] = '\0';
    // cout << phash_hexchar << endl;

    // for (int i = 0; i < SWIFFT_OUTPUT_BLOCK_SIZE; i++) {
    //     if (phash.data[i] != output1.data[i] ^ output2.data[i])
    //         cout << 0;
    // }

    // for (int i = 0; i < SWIFFT_OUTPUT_BLOCK_SIZE; i++) {
    // outhash.data[i] = 0;
    // outhash.data[i] += output1.data[i];
    // outhash.data[i] += output2.data[i];
    // outhash.data[i] += output3.data[i];
    // SWIFFT_Add(outhash.data, output1.data);
    // SWIFFT_Add(outhash.data, output2.data);
    // SWIFFT_Add(outhash.data, output3.data);
    // }
    // char outhash_hexchar[SWIFFT_OUTPUT_BLOCK_SIZE << 1 + 1];
    // char_to_hex(outhash.data, SWIFFT_OUTPUT_BLOCK_SIZE, outhash_hexchar);
    // outhash_hexchar[SWIFFT_OUTPUT_BLOCK_SIZE << 1] = '\0';
    // cout << outhash_hexchar << endl;

    // char phash_hexchar[SWIFFT_OUTPUT_BLOCK_SIZE << 1 + 1];
    // char_to_hex(phash.data, SWIFFT_OUTPUT_BLOCK_SIZE, phash_hexchar);
    // phash_hexchar[SWIFFT_OUTPUT_BLOCK_SIZE << 1] = '\0';
    // cout << phash_hexchar << endl;
    // cout << (outhash == phash) << endl;

    // SwifftInput* input = new SwifftInput[blkNum];
    // SwifftOutput* output = new SwifftOutput[blkNum];
    // cout << "finish new datas" << endl;
    auto pos0 = std::chrono::steady_clock::now();
    randomize(input, blkNum);
    auto pos1 = std::chrono::steady_clock::now();
    // SWIFFT_ComputeMultiple(blkNum, input[0].data, output[0].data);
    SWIFFT_ComputeMultiple_AVX2(blkNum, input[0].data, output[0].data);
    auto pos2 = std::chrono::steady_clock::now();
    std::chrono::duration<double> dur0 = pos1 - pos0;
    std::chrono::duration<double> dur1 = pos2 - pos1;
    cout << dur0.count() << " " << dur1.count() << endl;
}